package com.lagou.servlet;

import com.lagou.Annotion.*;
import com.lagou.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * @Authther: mrwu
 * @date:2021/3/21-03
 * @Description:com.lagou.servlet
 */
//封装转发请求类
//@WebServlet(urlPatterns = "/")
public class DispatchServlet extends HttpServlet {
    private Properties properties = new Properties();
    private List<String> classNames = new ArrayList<>();
    private HashMap<String,Object> beans = new HashMap<String,Object>();
    private List<Handler> handlerMapping = new ArrayList<>();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doGet(req, resp);
        doDispatchWithcheck(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);
        doDispatchWithcheck(req, resp);
    }

    /**
     * 根据白名单进行处理
     * @param req
     * @param resp
     * @throws IOException
     */
    private void  doDispatchWithcheck(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (docheck(req,getHandler(req))){
            doDispatch(req, resp);
        }else {
            resp.getWriter().write("meiyouquanxian");
            resp.getWriter().flush();
        }
    }


    @Override
    public void init(ServletConfig config) throws ServletException {
        //从web.xml中获取读到的配置文件
        String configInitParameter = config.getInitParameter("contextConfiglocation");
        //加载配置信息
        doloadConfig(configInitParameter);
        //扫描注解类
        doscan(properties.getProperty("scanpackage"));
        //调用无参构造实力化需要被存储的类
        doInstance();
        //对实例化的对象属性进行装配，一句是否被autowired注解
        doAutoWire();
        //将对应的URi 与实际处理对象，方法，参数顺序进行存储
        initHandlerMapping();

    }

    private void doloadConfig(String configInitParameter){
        InputStream resourceAsStream =
                this.getClass().getClassLoader().getResourceAsStream(configInitParameter);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //扫描方法
    private void doscan(String packege){
    String packagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()
            +packege.replaceAll("\\.", "/");
    File pack = new File(packagePath);
        for (File file : pack.listFiles()) {
            if (file.isDirectory()){
                doscan(packege+"."+file.getName());
            }else  if (file.getName().endsWith(".class")){
                String classname =packege+"."+file.getName().replaceAll(".class", "");

                classNames.add(classname);
            }
        }

    }
    //进行实例化
    private void doInstance(){
        for (String className : classNames) {
            Class<?> aClass = null;//获取类

            try {
                aClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                continue;
            }
            if (aClass.isInterface()||aClass.isAnnotation()){
                continue;
            }
            String  classSimpleName = aClass.getSimpleName();
            String value="";
            Object instance = null;
            String key="";

            //判断类是否被注解 表明里边会定义处理请求的方法
            if (aClass.isAnnotationPresent(MyController.class)){
                value = aClass.getAnnotation(MyController.class).value();
                try {
                    instance = aClass.newInstance();
                }catch (Exception e){
                    e.printStackTrace();
                }
                key =value.length()>0 ?value :classSimpleName.substring(0, 1).toLowerCase()+classSimpleName.substring(1);
                beans.put(key,instance);
            }else if (aClass.isAnnotationPresent(MyService.class)){
                value = aClass.getAnnotation(MyService.class).value();
                try {
                    instance = aClass.newInstance();
                }catch (Exception e){
                    e.printStackTrace();
                }
                key =value.length()>0 ?value :classSimpleName.substring(0, 1).toLowerCase()+classSimpleName.substring(1);
                beans.put(key,instance);
            }


        }
    }
    //处理依赖
    private void doAutoWire(){
    beans.forEach((key, value)->{
        for (Field field : value.getClass().getDeclaredFields()) {
            System.out.println(field.getName());
            if (field.isAnnotationPresent(MyAutoWired.class)){
                field.setAccessible(true);
                for (Object o : beans.values()) {
                    Class<?> type = field.getType();
                    if (type.isAssignableFrom(o.getClass())){
                        try {
                            field.set(value, o);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }


            }
        }

    });
    }
    private void doDispatch(HttpServletRequest req,HttpServletResponse resp){

        //获取要匹配的方法
        Handler handler = getHandler(req);
        if (handler ==null){
            return;
        }
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        Map<String, String[]> parameterMap = req.getParameterMap();
        //参数数组
        Object[] paravalues = new Object[parameterTypes.length];
        parameterMap.forEach((key,strings)->{
            //将字符串数组用,拼接
            String join = StringUtils.join(strings, ",");
           if (handler.getParamIndexMappings().containsKey(key)){
              //说明有这个参数
               Integer index = handler.getParamIndexMappings().get(key);
               paravalues[index]=join; //当前情况只适合基本数据类型及其包装类型
           }
        });
        Integer reqidx = handler.getParamIndexMappings().get(HttpServletRequest.class.getSimpleName());
        Integer rspidx = handler.getParamIndexMappings().get(HttpServletResponse.class.getSimpleName());
       if (reqidx!=null){
           paravalues[reqidx]=req;
       }
       if (rspidx!=null){
           paravalues[rspidx]=resp;
       }


        try {

            Object result = handler.getMethod().invoke(handler.getController(), paravalues);


            //对返回结果进行渲染
            resp.getWriter().write(result.toString());

        }catch (Exception e){
         e.printStackTrace();
        }


    }

    private void initHandlerMapping(){
        //遍历bean
        beans.forEach((key,value)->{
              String baseUrl ="";
            if (value.getClass().isAnnotationPresent(MyController.class)){
                MyResultMapping annotation = value.getClass().getAnnotation(MyResultMapping.class);
                if (annotation!=null){

                    baseUrl= annotation.value();

                    //遍历方法，将被resultMapping注解的方法放入
                    for (Method declaredMethod : value.getClass().getDeclaredMethods()) {
                      if (declaredMethod.isAnnotationPresent(MyResultMapping.class)){
                          MyResultMapping annotation1 = declaredMethod.getAnnotation(MyResultMapping.class);
                          String methodUri = annotation1.value();

                          String uri = baseUrl+methodUri;//获取定义的全uri
                          Handler handler = new Handler(value,declaredMethod,uri,new HashMap<>());

                          //获取参数
                          Parameter[] parameters = declaredMethod.getParameters();
                          for (int i = 0; i < parameters.length; i++) {

                              Class<?> type = parameters[i].getType();
                              //获取参数类型 后边通过类型匹配进行赋值
                              if (type == HttpServletRequest.class|| type == HttpServletResponse.class){
                                  //httprequst 或response 则不存全限定名
                                  handler.getParamIndexMappings().put(parameters[i].getType().getSimpleName(), i);
                              }else {
                                  handler.getParamIndexMappings().put(parameters[i].getName(),i);
                              }

                          }
                          //处理MySecurity 注解
                          String[] strings = parseMethodSe(declaredMethod);
                          if (strings !=null){
                              handler.getFilters().addAll(List.of(strings));
                          }
                          handlerMapping.add(handler);


                      }
                    }
                }

            }


        });


    }


    private Handler getHandler(HttpServletRequest req){
        //通过uri 匹配获得对应的handler
        String requestURI = req.getRequestURI();
//        String method = req.getMethod();

       for (Handler hander:handlerMapping){
           //当前方法无法处理restful 风格
           if (hander.getUrl().equalsIgnoreCase(requestURI)){
               return hander;
           }
       }

       return null;
    }

    /**
     * 获取被MySecurity 注解的值
     * @param method
     * @return
     */
    private String[] parseMethodSe(Method method){

        if (method.isAnnotationPresent(MySecurity.class)){
            String[] strings = method.getAnnotation(MySecurity.class).value();
            return strings;
        }
        return null;
    }

    /**
     *
     * @param req 当前的请求
     * @param handler 处理当前请求的handler 对象
     * @return 返回是否在白名单内 如果是true 则在，false 则不在
     */
   private boolean docheck(HttpServletRequest req,Handler handler){
       String[] names = req.getParameterMap().get("name");
       if (handler.getFilters().containsAll(List.of(names))){
           return true;
       }
       return false;
    }

}
